<template>
  <app-page :full="false">
    <div class="page">
      <div class="info">
        <div class="info-item" v-for="item in infos">
          <div class="label">{{ item.label }}</div>
          <div class="value">{{ item.value }}</div>
          <div class="icon" :style="{ backgroundColor: item.color }">
            <i :class="` iconfont icon-${item.icon}`"></i>
          </div>
        </div>
      </div>
      <div class="row">
        <AlertType :data="data.alertTypes" style="height: 360px" full scrollbar />
        <Attack :data="data.attacks" style="height: 360px" full scrollbar />
        <YpStat style="height: 360px" full scrollbar />
      </div>
      <app-card title="告警列表" style="margin-top: 16px">
        <div class="alert">
          <div class="alert-header">
            <el-form-item label="告警等级" style="width: 300px">
              <el-select
                v-model="data.query.level_code"
                filterable
                clearable
                placeholder="请选择"
                @change="handleSearch"
              >
                <el-option
                  v-for="(item, index) in [
                    { label: '全部', value: -1 },
                    { label: '高危', value: 2 },
                    { label: '中危', value: 3 },
                    { label: '低危', value: 4 },
                  ]"
                  :key="index"
                  :label="item.label"
                  :value="item.value"
                ></el-option>
              </el-select>
            </el-form-item>
          </div>

          <div class="alert-body">
            <div class="alert-item" v-for="item in data.list">
              <div class="header">
                <div class="host-name">{{ item.host_name }}</div>
                <!-- <div class="ip">{{ item.host_ip }}</div> -->
                <div class="level" :class="`level--${item.level}`">
                  {{
                    {
                      high: '高危',
                      middle: '中危',
                      low: '低危',
                    }[item.level] || item.level
                  }}
                </div>
                <div class="extra">
                  <el-button @click="handleDetail(item)">详情</el-button>
                  <el-button type="primary" @click="handleYp(item)" :loading="item.loading">研判</el-button>
                </div>
              </div>

              <div class="alert-type">{{ item.alert_type }}</div>
              <div class="meta">
                <div class="meta-item">
                  <div class="meta-item--label">IP</div>
                  <div class="meta-item--value">{{ item.host_ip }}</div>
                </div>
                <div class="meta-item">
                  <div class="meta-item--label">攻击阶段</div>
                  <div class="meta-item--value">{{ item.attack_stage }}</div>
                </div>
                <div class="meta-item">
                  <div class="meta-item--label">技术编号</div>
                  <div class="meta-item--value">{{ item.attack_code }}</div>
                </div>
                <div class="meta-item">
                  <div class="meta-item--label">告警时间</div>
                  <div class="meta-item--value">{{ item._alertTime }}</div>
                </div>
                <div class="meta-item">
                  <div class="meta-item--label">研判状态：</div>
                  <div class="meta-item--value">
                    <span v-if="item.assessment_status === 1" style="color: #4cc9f0; font-weight: bold">
                      正常行为
                    </span>
                    <span v-else-if="item.assessment_status === 2" style="color: #f72585; font-weight: bold">
                      攻击行为
                    </span>
                    <template v-else>
                      {{
                        {
                          0: '未研判',
                          1: '正常行为',
                          2: '攻击行为',
                        }[item.assessment_status]
                      }}
                    </template>
                  </div>
                </div>
                <div class="meta-item" v-if="item.trust_score != 0">
                  <div class="meta-item--label">置信度</div>
                  <div class="meta-item--value">{{ item.trust_score }}</div>
                </div>
                <!-- <span>攻击阶段: {{ item.attack_stage }}</span>
                <span>技术编号: {{ item.attack_code }}</span>
                <span>告警时间: {{ item._alertTime }}</span> -->
              </div>

              <el-popover placement="bottom" :width="300">
                <template #reference>
                  <div class="suggestion">
                    <!-- <span>处理建议:</span> -->
                    {{ item.suggestion }}
                  </div>
                </template>

                <div class="tips" v-html="item.suggestionHtml"></div>
              </el-popover>
            </div>
          </div>

          <el-pagination
            @size-change="handleSizeChange"
            @current-change="handlePageChange"
            v-model:current-page="data.page"
            v-model:page-size="data.pageSize"
            layout="total, sizes, prev, pager, next, jumper"
            :total="data.total"
            style="margin-top: var(--gutter); display: flex; justify-content: flex-end"
            :page-sizes="[9, 18, 36, 45, 90, 180]"
          />
        </div>
      </app-card>
    </div>

    <RawData v-model="rawData.show" :data="rawData.data" />
    <Trace v-model="dialog.trace.show" v-bind="dialog.trace" />
    <YpResult v-model="dialog.result.show" v-bind="dialog.result" />
  </app-page>
</template>
<script setup>
import axios from 'axios'
import http, { post } from '@/utils/http'
import { handleError } from 'vue'

import graphData from './graph.json'

import AlertType from './components/AlertType.vue'
import Attack from './components/Attack.vue'
import YpStat from './components/YpStat.vue'

import RawData from './components/RawData.vue'
import Trace from './components/Trace.vue'
import YpResult from './components/YpResult.vue'
// import Graph from './components/Graph.vue'

import dayjs from 'dayjs'

const rawData = reactive({
  show: false,
  data: null,
})

const dialog = reactive({
  trace: {
    show: false,
    data: null,
  },
  result: {
    show: false,
    data: null,
  },
})
// setTimeout(() => {
//   dialog.trace.show = true
// }, 2000)

const data = reactive({
  index: 1,
  alertSize: 0,

  total: 0,
  page: 1,
  pageSize: 9,
  list: [],

  high: 0,
  low: 0,
  middle: 0,

  alertTypes: [],

  ypId: null,
  chekResult: null,

  yp: {
    step: 0,
    data: [],
  },

  query: {
    level_code: -1,
  },

  // chekResult: {
  //   action:
  //     '当前告警事件中涉及多个进程的执行，其中包含多个`cmd`和`wmic.exe`进程，并且有多个`wmic`命令在执行与软件授权相关的查询，例如查询`softwarelicensingproduct`的授权信息、获取`graceperiodremaining`值、获取`licensefamily`值等。此外，有使用`netsh`修改IP地址的行为，且多个进程以系统权限运行，这表明存在潜在的系统管理操作行为。所有进程的父子关系显示部分进程可能由用户启动（如`cmd`进程），并进一步调用其他命令，存在潜在的命令链式行为。',
  //   topology:
  //     '告警事件中的进程拓扑关系如下：\n- `services`（PID:1080）→ `svchost`（PID:2592）→ `cleanmgr.exe`（PID:60240）。\n- `cmd`（PID:11996）→ `wmic.exe`（PID:58684）。\n- `cmd`（PID:16080）→ `wmic.exe`（PID:20068）。\n- `cmd`（PID:16864）→ `netsh.exe`（PID:49060）。\n- `cmd`（PID:30312）→ `wmic.exe`（PID:30192）。\n- `cmd`（PID:43088）→ `wmic.exe`（PID:34352）。\n- `cmd`（PID:49068）→ `wmic.exe`（PID:27076）。\n- `cmd`（PID:53356）→ `wmic.exe`（PID:52932）。\n- `cmd`（PID:61380）→ `wmic.exe`（PID:59980）。\n- 其他`cmd`进程如PID:46484、PID:53140、PID:4264等存在与其他进程的连接，但未形成明显的恶意命令链。',
  //   topology_draw:
  //     '{"attack_code":"","topologyid":{"1080":{"comm":"services","exe":"","pid":1080,"ppid":0,"argv":"","username":"","timestamp":""},"11996":{"comm":"cmd","exe":"","pid":11996,"ppid":0,"argv":"","username":"","timestamp":""},"16080":{"comm":"cmd","exe":"","pid":16080,"ppid":0,"argv":"","username":"","timestamp":""},"16864":{"comm":"cmd","exe":"","pid":16864,"ppid":0,"argv":"","username":"","timestamp":""},"19560":{"comm":"kwifi","exe":"","pid":19560,"ppid":0,"argv":"","username":"","timestamp":""},"20068":{"comm":"wmic.exe","exe":"C:\\\\windows\\\\system32\\\\wbem\\\\wmic.exe","pid":20068,"ppid":16080,"argv":"wmic  path softwarelicensingproduct where id=\'85dd8b5f-eaa4-4af3-a628-cce9e77c9a03\' get graceperiodremaining /value ","username":"NT AUTHORITY\\\\SYSTEM","timestamp":"2025-04-27 12:01:33"},"2592":{"comm":"svchost","exe":"","pid":2592,"ppid":0,"argv":"","username":"","timestamp":""},"27076":{"comm":"wmic.exe","exe":"C:\\\\windows\\\\system32\\\\wbem\\\\wmic.exe","pid":27076,"ppid":49068,"argv":"wmic  path win32_computersystem get creationclassname /value  ","username":"NT AUTHORITY\\\\SYSTEM","timestamp":"2025-04-27 12:00:22"},"30192":{"comm":"wmic.exe","exe":"C:\\\\windows\\\\system32\\\\wbem\\\\wmic.exe","pid":30192,"ppid":30312,"argv":"wmic  path softwarelicensingproduct where (name like \'%windows%\' and description like \'%kmsclient%\' and partialproductkey is not null and licensedependson is null) get id /value ","username":"NT AUTHORITY\\\\SYSTEM","timestamp":"2025-04-27 12:00:23"},"30312":{"comm":"cmd","exe":"","pid":30312,"ppid":0,"argv":"","username":"","timestamp":""},"34352":{"comm":"wmic.exe","exe":"C:\\\\windows\\\\system32\\\\wbem\\\\wmic.exe","pid":34352,"ppid":43088,"argv":"wmic  path officesoftwareprotectionproduct where (name like \'%office%\' and description like \'%kmsclient%\' and partialproductkey is not null and licensedependson is null) get id /value ","username":"NT AUTHORITY\\\\SYSTEM","timestamp":"2025-04-27 12:00:23"},"4264":{"comm":"knatsvc","exe":"","pid":4264,"ppid":0,"argv":"","username":"","timestamp":""},"43088":{"comm":"cmd","exe":"","pid":43088,"ppid":0,"argv":"","username":"","timestamp":""},"46484":{"comm":"cmd","exe":"","pid":46484,"ppid":0,"argv":"","username":"","timestamp":""},"49060":{"comm":"netsh.exe","exe":"C:\\\\windows\\\\syswow64\\\\netsh.exe","pid":49060,"ppid":16864,"argv":"netsh  interface ipv4 set address name=\\"16\\" static 192.168.191.1 255.255.255.0","username":"CD-ANTIY\\\\taomingwei","timestamp":"2025-04-27 09:49:43"},"49068":{"comm":"cmd","exe":"","pid":49068,"ppid":0,"argv":"","username":"","timestamp":""},"52932":{"comm":"wmic.exe","exe":"C:\\\\windows\\\\system32\\\\wbem\\\\wmic.exe","pid":52932,"ppid":53356,"argv":"wmic  path softwarelicensingproduct where (name like \'%office%\' and description like \'%kmsclient%\' and partialproductkey is not null and licensedependson is null) get id /value ","username":"NT AUTHORITY\\\\SYSTEM","timestamp":"2025-04-27 12:00:23"},"53140":{"comm":"cmd","exe":"","pid":53140,"ppid":0,"argv":"","username":"","timestamp":""},"53356":{"comm":"cmd","exe":"","pid":53356,"ppid":0,"argv":"","username":"","timestamp":""},"58684":{"comm":"wmic.exe","exe":"C:\\\\windows\\\\system32\\\\wbem\\\\wmic.exe","pid":58684,"ppid":11996,"argv":"wmic  path softwarelicensingproduct where id=\'85dd8b5f-eaa4-4af3-a628-cce9e77c9a03\' get licensefamily /value ","username":"NT AUTHORITY\\\\SYSTEM","timestamp":"2025-04-27 12:00:23"},"59980":{"comm":"wmic.exe","exe":"C:\\\\windows\\\\system32\\\\wbem\\\\wmic.exe","pid":59980,"ppid":61380,"argv":"wmic  path softwarelicensingproduct where (applicationid=\'55c92734-d682-4d71-983e-d6ec3f16059f\') get id /value ","username":"NT AUTHORITY\\\\SYSTEM","timestamp":"2025-04-27 12:00:23"},"60240":{"comm":"cleanmgr.exe","exe":"C:\\\\windows\\\\system32\\\\cleanmgr.exe","pid":60240,"ppid":2592,"argv":"\\"c:\\\\windows\\\\system32\\\\cleanmgr.exe\\" /autocleanstoragesense /d c:","username":"CD-ANTIY\\\\taomingwei","timestamp":"2025-04-26 15:48:34"},"61380":{"comm":"cmd","exe":"","pid":61380,"ppid":0,"argv":"","username":"","timestamp":""},"8844":{"comm":"netsh.exe","exe":"C:\\\\windows\\\\syswow64\\\\netsh.exe","pid":8844,"ppid":53140,"argv":"netsh  wlan show drivers ","username":"CD-ANTIY\\\\taomingwei","timestamp":"2025-04-27 09:49:34"},"936":{"comm":"wininit","exe":"","pid":936,"ppid":0,"argv":"","username":"","timestamp":""}},"related":[{"start":2592,"end":60240},{"start":1080,"end":2592},{"start":936,"end":1080},{"start":53140,"end":8844},{"start":19560,"end":53140},{"start":4264,"end":19560},{"start":1080,"end":4264},{"start":936,"end":1080},{"start":16864,"end":49060},{"start":19560,"end":16864},{"start":4264,"end":19560},{"start":1080,"end":4264},{"start":936,"end":1080},{"start":49068,"end":27076},{"start":46484,"end":49068},{"start":2592,"end":46484},{"start":1080,"end":2592},{"start":936,"end":1080},{"start":61380,"end":59980},{"start":46484,"end":61380},{"start":2592,"end":46484},{"start":1080,"end":2592},{"start":936,"end":1080},{"start":30312,"end":30192},{"start":46484,"end":30312},{"start":2592,"end":46484},{"start":1080,"end":2592},{"start":936,"end":1080},{"start":53356,"end":52932},{"start":46484,"end":53356},{"start":2592,"end":46484},{"start":1080,"end":2592},{"start":936,"end":1080},{"start":43088,"end":34352},{"start":46484,"end":43088},{"start":2592,"end":46484},{"start":1080,"end":2592},{"start":936,"end":1080},{"start":11996,"end":58684},{"start":46484,"end":11996},{"start":2592,"end":46484},{"start":1080,"end":2592},{"start":936,"end":1080},{"start":16080,"end":20068},{"start":46484,"end":16080},{"start":2592,"end":46484},{"start":1080,"end":2592},{"start":936,"end":1080}]}',
  //   attck:
  //     '分析发现多个`wmic.exe`命令涉及对Windows许可证相关数据的查询，包括`softwarelicensingproduct`的`id`、`licensefamily`、`graceperiodremaining`等字段，该行为与系统信息收集和授权状态查询密切相关。结合`netsh`命令修改IP地址的行为，可能是用于网络配置调整。这些行为虽未明确指向恶意脚本的执行，但频繁使用系统管理工具和`cmd`命令执行多个授权相关查询，结合`wmic`执行命令的权限为`NT AUTHORITY\\SYSTEM`，存在可疑行为。尽管未发现直接的脚本执行、异常模块加载或云文件下载等行为，但命令组合行为与APT攻击中常见的系统信息收集和网络配置调整行为高度一致。',
  //   result: 'attack',
  // },
})

// getYpData(data.chekResult)
// setTimeout(() => {
//   getYpData(data.chekResult)
// }, 2000)

function getYpData(result) {
  data.yp.topology_draw = JSON.parse(result.topology_draw)

  const text = result.result.replace('\n', '')

  if (text === 'normal') {
    result.result = '正常行为'
  } else {
    result.result = '攻击行为'
  }

  const ary = []
  for (const key in result) {
    const title = {
      action: '行为分析',
      topology: '级联拓扑分析',
      attck: '攻击行为分析',
      result: '研判结果',
    }[key]

    if (!title) {
      continue
    }

    let value = result[key]

    if (key == 'result') {
      const text = value.replace('\n', '')

      if (text === 'normal') {
        value = '正常行为'
      } else {
        value = '攻击行为'
      }
    }

    ary.push({
      type: key,
      title,
      value: result[key],
    })
  }

  data.yp.step = 0
  data.yp.data = ary
}

const infos = computed(() => {
  return [
    { label: '告警总数', value: data.alertSize, color: '#EF4444', icon: 'safety' },
    { label: '高危告警', value: data.high, color: '#EF4444', icon: 'safety' },
    { label: '中危告警', value: data.low, color: '#EAB308', icon: 'safety' },
    { label: '低危告警', value: data.middle, color: '#3B82F6', icon: 'safety' },
  ]
})

function hexToRgba(hex, alpha = 1) {
  // 去掉 # 号
  hex = hex.replace('#', '')

  // 解析红绿蓝值
  let r = parseInt(hex.substring(0, 2), 16)
  let g = parseInt(hex.substring(2, 4), 16)
  let b = parseInt(hex.substring(4, 6), 16)

  // 返回 RGBA 颜色
  return `rgba(${r}, ${g}, ${b}, ${alpha})`
}

http.get('/hids/alert/size').then(res => {
  data.alertSize = res.total_size
})

// 技战术统计
http.get('/hids/alert/attckid').then(res => {
  data.attacks = res
})

// 告警类型统计
http.get('/hids/alert/type').then(res => {
  data.alertTypes = res
})

// 告警等级统计
http.get('/hids/alert/level').then(res => {
  for (const key in res) {
    const item = res[key]
    data[
      {
        2: 'high',
        3: 'middle',
        4: 'low',
      }[item.level]
    ] = item.size
  }
})

function getAlertList() {
  // 告警列表
  http
    .post('/hids/alert/search', {
      pagenum: data.page,
      pagesize: data.pageSize,
      ...data.query,
    })
    .then(res => {
      data.total = res.total_size
      data.list = res.hids_evts.map(item => {
        if (item.warning_date) {
          item._alertTime = dayjs(item.warning_date).format('YYYY-MM-DD HH:mm:ss')
        }

        if (item.suggestion) {
          item.suggestionHtml = item.suggestion
            .split('$$$$')
            .map(val => `<p>${val}</p>`)
            .join('\n')

          item.suggestion = item.suggestion.split('$$$$').join('\n')
        }

        item.loading = false

        item._id = data.index++

        return item
      })
    })
}

getAlertList()

const handleYp = async item => {
  dialog.result.show = true
  dialog.result.data = item
}

const handleSizeChange = pageSize => {
  data.page = 1
  data.pageSize = pageSize
  getAlertList(true)
}

// 分页切换
const handlePageChange = page => {
  data.page = page
  getAlertList()
}

const handleSearch = () => {
  data.page = 1
  getAlertList()
}

const handleDetail = item => {
  console.log('handleDetail')
  rawData.data = item
  rawData.show = true
}

const handleSource = item => {
  dialog.trace.show = true
  dialog.trace.data = null
  dialog.trace.loading = true

  http
    .post('/hids/alert/trace', data.yp.topology_draw)
    .then(res => {
      res.analysis = res.analysis.replace('```analysis\n', '').replace('\n```\n', '')

      console.log('res.analysis', res.analysis)

      dialog.trace.data = res
    })
    .finally(() => {
      dialog.trace.loading = false
    })
}
</script>
<style lang="scss" scoped>
// .main {
//   position: sticky;
//   padding-top: 16px;
//   overflow: hidden;
//   height: 100vh;
//   display: flex;
//   .warp {
//     width: 100%;
//     display: flex;
//     gap: 16px;
//     padding-top: 16px;
//   }
// }

.flow {
  width: calc(25% - 16px);
}

.info {
  display: flex;
  gap: 16px;
  margin-bottom: 16px;
}

.info-item {
  flex: 1;
  background-color: #fff;
  box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12);
  border-radius: 8px;
  padding: 24px;
  position: relative;
  .value {
    font-weight: bold;
    font-size: 30px;
  }
  .icon {
    position: absolute;
    top: 32px;
    right: 24px;
    width: 48px;
    height: 48px;
    border-radius: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #fff;
    i {
      font-size: 24px;
    }
  }
}

.check-item {
  border: 1px solid #e2e8f0;
  padding: 20px;
  border-radius: 12px;
  margin-bottom: 16px;
  .title {
    font-size: 14px;
    font-weight: 600;
    margin-bottom: 12px;
  }
}

.check-item--attack {
  background-color: rgba($color: #ef4444, $alpha: 0.1);
  border: 1px solid rgba($color: #ef4444, $alpha: 0.2);
  color: #ef4444;
}

.check-item--safe {
  background-color: rgba($color: #67c23a, $alpha: 0.1);
  border: 1px solid rgba($color: #67c23a, $alpha: 0.2);
  color: #67c23a;
}

.button-group {
  display: flex;
  margin-bottom: 12px;
}

.alert-item {
  border: 1px solid #e2e8f0;
  padding: 20px;
  border-radius: 12px;
  overflow: hidden;
  &:hover {
    transition-property: all;
    transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
    transition-duration: 150ms;
    background-color: #f8fafc;
    box-shadow:
      rgba(0, 0, 0, 0) 0px 0px 0px 0px,
      rgba(0, 0, 0, 0) 0px 0px 0px 0px,
      rgba(0, 0, 0, 0.1) 0px 4px 6px -1px,
      rgba(0, 0, 0, 0.1) 0px 2px 4px -2px;
  }

  .header {
    display: flex;
    align-items: center;
    margin-bottom: 12px;
    gap: 12px;

    .host-name {
      font-size: 18px;
      font-weight: 600;
    }

    .ip {
      padding: 4px 8px;
      color: #64748b;
      background-color: #f1f5f9;
    }

    .level {
      width: 46px;
      line-height: 22px;
      text-align: center;
      border-radius: 23px;
      font-size: 12px;
      font-weight: 600;
    }

    .level--low {
      border: 1px solid #c0dbfe;
      color: #2563eb;
    }

    .level--middle {
      background-color: #f59e0b;
      color: #fff;
    }

    .level--high {
      background-color: #ef4444;
      color: #fff;
    }

    .extra {
      flex: 1;
      text-align: right;
    }
  }

  .suggestion {
    background-color: #eff6ff;
    color: #1e40af;
    padding: 12px;
    border-radius: 8px;
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
    border: 1px solid #bfdbfe;
    span {
      font-weight: 500;
    }
  }

  .alert-type {
    margin-bottom: 12px;
    font-weight: 500;
    color: #334155;
    font-size: 16px;
  }

  .meta {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
    margin-bottom: 12px;
    .meta-item {
    }
    .meta-item--label {
      font-size: 12px;
      color: #999;
      margin-bottom: 3px;
    }
    .meta-item--value {
      color: #333;
    }
  }
}

.row {
  display: flex;
  gap: 16px;
  .card {
    flex: 1;
  }
}

.list-enter-active,
.list-leave-active {
  transition: all 2s ease;
}
.list-enter-from,
.list-leave-to {
  opacity: 0;
  transform: translateX(30px);
}

.attack {
  display: flex;
  align-items: center;
}

.alert {
  display: flex;
  flex-direction: column;
  overflow: hidden;

  .alert-body {
    flex: 1;
    overflow: hidden;
    display: grid;
    gap: 24px;
    grid-template-columns: repeat(3, 1fr);
  }
}

.tips {
  // white-space: pre;
  // white-space: nowrap;
}
</style>
